import*as THREE from 'three'

const particleRes = 1024

import {updatePosition,quadVert,streakVertex,common} from "./shaders.js"

const sceneRTT = new THREE.Scene();

let debugMesh;

const debugPlaneGeometry = new THREE.PlaneGeometry(1,1,1);
const debugPlaneMaterial = new THREE.MeshBasicMaterial({
  wireframe: false,
  color: 0xffffff,
  side: THREE.DoubleSide
});
debugMesh = new THREE.Mesh(debugPlaneGeometry,debugPlaneMaterial);
debugMesh.position.set(0, 0, -1.5);
debugMesh.visible = false;

const windFiles = {
  0: '2016112000',
  6: '2016112006',
  12: '2016112012',
  18: '2016112018',
  24: '2016112100',
  30: '2016112106',
  36: '2016112112',
  42: '2016112118',
  48: '2016112200'
};

async function loadWindData(name) {
  return new Promise( (resolve, reject) => {
        fetch('../../wind_field_flat/assets/wind/' + name + '.json').then(windData => windData.json().then(windData => {
              const windImage = new Image();
              windData.image = windImage;
              windImage.src = '../../wind_field_flat/assets/wind/' + name + '.png';
              windImage.onload = function() {
                resolve(windData);
              }
            }
        ))
      }
  )
}


let windUniforms = {
  u_wind_res:{value:null},
  u_wind:{value:null},
  u_wind_min:{value:null},
  u_wind_max:{value:null}
}
let windTexture;
let loadWind=async (fn)=>{
  const windData = await loadWindData(fn)

  console.log(windData)
  if(windTexture)windTexture.dispose()
  windTexture = new THREE.Texture(windData.image);
  windTexture.magFilter = THREE.LinearFilter;
  windTexture.minFilter = THREE.LinearFilter;
  windTexture.wrapS = THREE.RepeatWrapping;//.ClampToEdgeWrapping;
  windTexture.wrapT = THREE.RepeatWrapping;//.ClampToEdgeWrapping;
  windTexture.format = THREE.RGBAFormat;
  windTexture.needsUpdate = true;
  windUniforms.u_wind_res.value={
    x: windData.width,
    y: windData.height
  }
  windUniforms.u_wind.value=windTexture;
  windUniforms.u_wind_min.value={
    x: windData.uMin,
    y: windData.vMin
  }
  windUniforms.u_wind_max.value={
    x: windData.uMax,
    y: windData.vMax
  }
}
const windFileKey = 0
const windFileName = windFiles[windFileKey];

const _numParticles = particleRes * particleRes;

const particleState = new Uint8Array(_numParticles * 4);
for (let i = 0; i < particleState.length; i++)
  particleState[i] = Math.floor(Math.random() * 256);

const pointsArr = new Float32Array(2 * 3);

const pointsBuffer = new THREE.InstancedBufferAttribute(pointsArr,3)

const opt = {
  magFilter: THREE.NearestFilter,
  minFilter: THREE.NearestFilter,
  format: THREE.RGBAFormat,
  //type: THREE.FloatType,
  type: THREE.UnsignedByteType,
  depthBuffer: false
}
const particleStateTarget0 = new THREE.WebGLRenderTarget(particleRes,particleRes,opt);
const particleStateTarget1 = new THREE.WebGLRenderTarget(particleRes,particleRes,opt);
const particleInitialStateTexture = new THREE.DataTexture(particleState,particleRes,particleRes,THREE.RGBAFormat,THREE.UnsignedByteType);
particleInitialStateTexture.minFilter = THREE.NearestFilter;
particleInitialStateTexture.magFilter = THREE.NearestFilter;
particleInitialStateTexture.needsUpdate = true;

const WindParam = {
  fadeOpacity: 0.996,// how fast the particle trails fade on each frame
  speedFactor: .25,// how fast the particles move
  dropRate: 0.003,// how often the particles move to a random place
  dropRateBump: 0.01,// drop rate increase relative to individual particle speed
}

const plane = new THREE.PlaneGeometry(window.innerWidth,window.innerHeight);

let uniforms = {
}

let declareUniform=(name,value)=>uniforms[name]={value}
declareUniform('u_particles',particleInitialStateTexture);
declareUniform('u_particles_res',particleRes);

Object.assign(uniforms,windUniforms)

declareUniform('u_speed_factor',WindParam.speedFactor)
declareUniform('u_drop_rate',WindParam.dropRate)
declareUniform('u_drop_rate_bump',WindParam.dropRateBump)
declareUniform('u_rand_seed',Math.random())
declareUniform('u_spherize',1.)
declareUniform('particleStateTarget0', null)
declareUniform('particleStateTarget1', null)
declareUniform('u_palA',new THREE.Vector3())
declareUniform('u_palB',new THREE.Vector3())
declareUniform('u_palC',new THREE.Vector3())
declareUniform('u_palD',new THREE.Vector3())
declareUniform('u_palScale',0.666)
declareUniform('u_palBias',0)
declareUniform('u_palBoost',new THREE.Color('black'))


let p = [
  0.5, 0.5, 0.5,		0.5, 0.5, 0.5,	1.0, 1.0, 1.0,	0.00, 0.33, 0.67,
  0.5, 0.5, 0.5,		0.5, 0.5, 0.5,	1.0, 1.0, 1.0,	0.00, 0.10, 0.20,
  0.5, 0.5, 0.5,		0.5, 0.5, 0.5,	1.0, 1.0, 1.0,	0.30, 0.20, 0.20,
  0.5, 0.5, 0.5,		0.5, 0.5, 0.5,	1.0, 1.0, 0.5,	0.80, 0.90, 0.30,
  0.5, 0.5, 0.5,		0.5, 0.5, 0.5,	1.0, 0.7, 0.4,	0.00, 0.15, 0.20,
  0.5, 0.5, 0.5,		0.5, 0.5, 0.5,	2.0, 1.0, 0.0,	0.50, 0.20, 0.25,
  0.8, 0.5, 0.4,		0.2, 0.4, 0.2,	2.0, 1.0, 1.0,	0.00, 0.25, 0.25,
]

let setPalette=(id)=>{
  let i=id*12;
  uniforms.u_palA.value.set(p[i++],p[i++],p[i++]);
  uniforms.u_palB.value.set(p[i++],p[i++],p[i++]);
  uniforms.u_palC.value.set(p[i++],p[i++],p[i++]);
  uniforms.u_palD.value.set(p[i++],p[i++],p[i++]);
  streaks.material.uniformsNeedUpdate = true;
}


const simUpdateMaterial = new THREE.ShaderMaterial({
  uniforms,
  depthTest: false,
  depthWrite: false,
  transparent: false,
  vertexShader: quadVert,
  fragmentShader: updatePosition
})

await loadWind(windFileName);

const cameraRTT = new THREE.OrthographicCamera(-1,1,1,-1,0,1);
//copied from RenderPass
class FullscreenTriangleGeometry extends THREE.BufferGeometry {
  constructor() {
    super();
    this.setAttribute('position', new THREE.Float32BufferAttribute([-1, 3, 0, -1, -1, 0, 3, -1, 0],3));
    this.setAttribute('uv', new THREE.Float32BufferAttribute([0, 2, 0, 0, 2, 0],2));
  }
}
const _geometry = new FullscreenTriangleGeometry()
// end copy from

const quad2 = new THREE.Mesh(_geometry,simUpdateMaterial);
sceneRTT.add(quad2);

let frame = 0;
let targets = [particleStateTarget0,particleStateTarget1];
function drawUpdateParticles(renderer) {
  const target=targets[frame&1];
  frame++;
  //const target = findRenderTargetNot(simUpdateMaterial.uniforms.u_particles.value)
  renderer.setRenderTarget(target);
  // renderer.clear();
  renderer.render(sceneRTT, cameraRTT);

  //uniforms.u_spherize.value = (Math.sin(performance.now()/1000)+1)*.5;

  uniforms.u_rand_seed.value = Math.random();
  uniforms.u_particles.value = targets[(frame+1)&1].texture
  //findRenderTargetNot(uniforms.u_particles.value).texture;
}

debugMesh && (debugMesh.material.map = particleStateTarget0.texture);

//Sim visualization
const pointsWindGeo = new THREE.InstancedBufferGeometry();
pointsWindGeo.instanceCount = particleRes * particleRes;
pointsWindGeo.setAttribute('position', pointsBuffer)
let exports;
let streaks;
let rebuildVisualizer=(usePoints=false)=>{
  let parent;
  if(streaks){
    parent = streaks.parent;
    streaks.removeFromParent();
  }
  if(usePoints){
    streaks = new THREE.Points(pointsWindGeo);
  }else{
    streaks = new THREE.LineSegments(pointsWindGeo);
  }
  streaks.material.size = .001;
  if(parent)parent.add(streaks)
  exports&&(exports.streaks=streaks);
  streaks.frustumCulled = false;
  streaks.frustumCulled = false;
  streaks.uniformCache = uniforms;


  streaks.material.onBeforeCompile = function(shader, renderer) {
    let patch = (vs, tok, tokfn) => shader[vs] = shader[vs].replace(tok, tokfn(tok))
    patch('vertexShader', '#include <common>', tok => `
               ${tok}
               ${common}
               `)
    patch('vertexShader', '#include <begin_vertex>', tok => `
                ${streakVertex}
               //${tok}`)
    patch('fragmentShader', '#include <common>', tok => `
               ${common}
               ${tok}
        `);
    patch('fragmentShader', '#include <color_fragment>', tok => `
               ${tok}
               diffuseColor.rgb = p_color;
        `)
    Object.assign(shader.uniforms,uniforms);
  }
  let frameCounter=0;
  streaks.onBeforeRender=()=>{
    frameCounter++;
    uniforms.particleStateTarget0.value = targets[frameCounter&1].texture;
    uniforms.particleStateTarget1.value = targets[(frameCounter+1)&1].texture;
  }
}
rebuildVisualizer();
setPalette(0);

exports = {streaks,uniforms,drawUpdateParticles,debugMesh,windFiles,loadWind,setPalette,rebuildVisualizer}

export default exports;
